
#include <BFont>
#include <BTime>
#include <BWidget>
#include <BSpring>
#include <BReadWrite>
#include <BSystem>
#include <BDisk>
#include <BStyle>
#include "member_BFileDialog.h"

using namespace BWE;

member_BFileDialog::member_BFileDialog(BFileDialog* fileDialog)
{
	boss = fileDialog;
	dirty = true;
	dirtyStyle = false;
	filter = BFolder::Filter_All;
	selectMode = Select_Single;
	style = 0;

	BHBoxLayout* topLayout = new BHBoxLayout();
	topLayout->setSpace(3);
	{
		backButton = new BButton("<");
		backButton->setMinSize(22, 0);
		frontButton = new BButton(">");
		frontButton->setMinSize(22, 0);
		aboveButton = new BButton("^");
		aboveButton->setMinSize(22, 0);
		pathComboBox = new BComboBox();
		searchComboBox = new BComboBox();

		topLayout->addWidget(backButton);
		topLayout->addWidget(frontButton);
		topLayout->addWidget(aboveButton);
		topLayout->addWidget(pathComboBox);
		topLayout->addWidget(searchComboBox);
	}

	treeWidget = new BTreeWidget();

	tableWidget = new BTableWidget();
	tableWidget->setSelectMode(selectMode);
	tableWidget->setSortable(true);
	tableWidget->setColumnCount(3);
	tableWidget->setColumnLabel(0, "Name");
	tableWidget->setColumnLabel(1, "Type");
	tableWidget->setColumnLabel(2, "Date");
	tableWidget->setSizeStretch(2);
	tableWidget->setColumnWidth(0, 300);

	BSplitter* splitter = new BSplitter();
	splitter->setSizeStretch(1000);
	splitter->addWidget(treeWidget);
	splitter->addWidget(tableWidget);

	BHBoxLayout* nameLayout = new BHBoxLayout();
	nameLayout->setSpace(3);
	{
		BLabel* nameLabel = new BLabel("name");
		fileNameEdit = new BLineEdit();
		fileNameEdit->setWidthStretch(100);
		nameLayout->addSpring(new BSpring(100));
		nameLayout->addWidget(nameLabel);
		nameLayout->addWidget(fileNameEdit);
	}

	okButton = new BButton("Ok");
	okButton->setMinSize(BSize(48, 0));
	cancleButton = new BButton("Cancel");
	cancleButton->setMinSize(BSize(48, 0));
	BHBoxLayout* buttonLayout = new BHBoxLayout();
	buttonLayout->setSpace(3);
	buttonLayout->addSpring();
	buttonLayout->addWidget(okButton);
	buttonLayout->addWidget(cancleButton);

	BVBoxLayout* layout = new BVBoxLayout(boss);
	layout->setSpace(3);
	layout->addLayout(topLayout);
	layout->addWidget(splitter);
	layout->addLayout(nameLayout);
	layout->addLayout(buttonLayout);

	connect(pathComboBox, Signal_CurrentChanged, &member_BFileDialog::procPathChanged);
	connect(treeWidget, Signal_ItemClicked, &member_BFileDialog::procOpenFolder);
	connect(treeWidget, Signal_ItemDoubleClicked, &member_BFileDialog::procExpandFolder);
	connect(tableWidget, Signal_ItemClicked, &member_BFileDialog::procFileClicked);
	connect(tableWidget, Signal_ItemDoubleClicked, &member_BFileDialog::procFileDoubleClicked);
	connect(fileNameEdit, Signal_Edited, &member_BFileDialog::procFileNameEdited);
	connect(okButton, Signal_Clicked, &member_BFileDialog::procOkClicked);
	connect(cancleButton, Signal_Clicked, &member_BFileDialog::procCancleClicked);

	resetTreeWidget();
}
member_BFileDialog::~member_BFileDialog()
{

}

BString member_BFileDialog::typeName(BEntry* entry)
{
	switch (entry->type())
	{
	case Entry_File:
	{
		const BString& fileName = entry->name();
		if (fileName.endsWith(".cpp"))
			return "C++ Source";
		if (fileName.endsWith(".png") || fileName.endsWith(".jpg"))
			return "Image";
		return "Unknown";
	}
	case Entry_Folder:
		return "Folder";
	case Entry_Link:
		return "Line";
	case Entry_Pipe:
		return "Pipe";
	case Entry_Regular:
		return "Regular";
	case Entry_Block:
		return "Block";
	case Entry_Char:
		return "Char";
	case Entry_Socket:
		return "Socket";
		break;
	default:
		break;
	}
	return "*";
}
BImage* member_BFileDialog::imageOf(BObject* object)
{
	if (style)
	{
		BImage* image = 0;
		if (BDisk* disk = dynamic_cast<BDisk*>(object))
		{
			if (disk->type() == BDisk::Removable)
				image = style->image("disk-usb");
			if (disk->type() == BDisk::Remote)
				image = style->image("disk-cloud");
			if (disk->type() == BDisk::CDRom)
				image = style->image("disk-cd");
			image = style->image("disk");
		}
		if (!image)
		{
			if (BEntry* entry = dynamic_cast<BEntry*>(object))
			{
				if (entry->type() == Entry_Folder)
					image = style->image("folder");
			}
		}
		if (!image)
		{
			BString imageName("file");
			const char* ext = object->name().ext();
			if (ext)
			{
				imageName << '-' << ext;
				imageName.lower();
				image = style->image(imageName);
			}
		}
		if (!image)
		{
			const BFont* font = boss->font();
			if (font)
			{
				image = (BImage*)font->bitmap('?');
			}
		}
		if (image)
			return image;
	}
	static BImage* image = new BImage();
	return image;
}

void member_BFileDialog::resetTreeWidget()
{
	if (dirty)
	{
		for (int i = 0; i < BSystem::GetDriverNumber(); i++)
		{
			BDisk* disk = BSystem::GetDriver(i);
			BString name = disk->name();
			name.remove('\\');
			name.remove('/');
			BTreeItem* topItem = treeWidget->addTopItem(name);
			topItem->setThing(disk);
			topItem->setImage(imageOf(disk));
		}
		dirty = false;
	}
}
void member_BFileDialog::freshTreeItemImage(BTreeItem* item)
{
	BEntry* entry = (BEntry*)item->thing();
	BImage* image = imageOf(entry);
	item->setImage(image);

	for (int i = 0; i < item->childCount(); i++)
	{
		BTreeItem* child = item->child(i);
		freshTreeItemImage(child);
	}
}
void member_BFileDialog::freshTableItemImage()
{
	for (int r = 0; r < tableWidget->rowCount(); r++)
	{
		BTableItem* item = tableWidget->item(r, 0);
		BEntry* entry = (BEntry*)item->thing();
		BImage* image = imageOf(entry);
		item->setImage(image);
	}
}

bool member_BFileDialog::locate(const BString& path)
{
	BString current = BFolder::FullPath(path);

	current.replace('\\', '/');
	BStringList namelst = current.split('/');
	BTreeItem* item = treeWidget->item(namelst.first());
	namelst.remove(0);
	while (item && namelst.size())
	{
		expandFolderItem(item);
		item = item->child(namelst.first());
		namelst.remove(0);
	}

	if (!item)
		return false;

	item->setSelected(true);

	BString itemPath = item->path();
	if (pathComboBox->currentText() == itemPath)
		return true;

	pathComboBox->addItem(itemPath);
	pathComboBox->setCurrentText(itemPath);

	return true;
}
void member_BFileDialog::expandFolderItem(BTreeItem* item)
{
	if (item->childCount() > 0)
		return;
	BFolder folder(item->path());
	if (!folder.open())
		return;
	for (int i = 0; i < folder.entryCount(); i++)
	{
		BEntry* entry = folder.entry(i);
		if (entry->type() == Entry_Folder)
		{
			const BString& name = entry->name();
			BTreeItem* child = new BTreeItem(name);
			child->setThing(entry);
			child->setImage(imageOf(entry));
			item->addChild(child);
		}
	}
	item->expand();
}
void member_BFileDialog::openFolder(const BString& path)
{
	if (path.empty())
	{
		locate("./");
		return;
	}
	BFolderHolder folder = new BFolder(path, filter);
	if (folder->open())
	{
		tableWidget->setRowCount(folder->entryCount());
		for (int i = 0; i < folder->entryCount(); i++)
		{
			BEntry* entry = folder->entry(i);
			const BString& name = entry->name();
			BString typeName = this->typeName(entry);
			const BTime& time = entry->time();
			BString timeStr;
			timeStr << time.years() << '-' << time.months() << '-' << time.days() << ' ' << time.hours() << ':' << time.minutes() << ':' << time.seconds();

			BTableItem* item = new BTableItem(name);
			item->setEditable(true);
			item->setThing(entry);
			item->setImage(imageOf(entry));
			tableWidget->setItem(i, 0, item);
			tableWidget->setItem(i, 1, typeName);
			tableWidget->setItem(i, 2, timeStr);
			tableWidget->setRowLabel(i, i);
		}
		tableWidget->sort();
		tableWidget->fresh();
		fileNameEdit->setText(BString());
		tableWidget->scroll(0);
	}
}

void member_BFileDialog::procPathChanged(BObject* object, const BValue& value)
{
	const BString& path = pathComboBox->currentText();
	openFolder(path);
}
void member_BFileDialog::procOpenFolder(BObject* object, const BValue& value)
{
	BTreeItem* item = value;
	if (!item)
		return;
	BString path = item->path();
	pathComboBox->addItem(path);
	pathComboBox->setCurrentText(path);
}
void member_BFileDialog::procExpandFolder(BObject* object, const BValue& value)
{
	BTreeItem* item = value;
	if (!item)
		return;
	expandFolderItem(item);
}
void member_BFileDialog::procFileClicked(BObject* object, const BValue& value)
{
	BString text;
	int count = tableWidget->selectedItemCount();
	for (int i = 0; i < count; i++)
	{
		BTableItem* item = tableWidget->selectedItem(i);
		const BString& fileName = item->text();
		text << fileName << ',';
	}
	if (text.size())
		text -= ",";
	fileNameEdit->setText(text);
	okButton->setAble(text.size());
}
void member_BFileDialog::procFileDoubleClicked(BObject* object, const BValue& value)
{
	BTableItem* item = value;
	if (item)
	{
		fileNameEdit->setText(item->text());
		BEntry* entry = (BEntry*)item->thing();
		if (entry->type() == Entry_Folder)
		{
			locate(entry->path());
		}
		else
		{
			fileNames.clear();
			fileNames << pathComboBox->currentText() + '/' + item->text();
			boss->done(1);
		}
	}
}
void member_BFileDialog::procFileNameEdited(BObject* object)
{

}
void member_BFileDialog::procOkClicked()
{
	fileNames.clear();
	if (fileNameEdit->text().size())
	{
		fileNames << pathComboBox->currentText() + '/' + fileNameEdit->text();
	}
	if (fileNames.size())
		boss->done(1);
	else
		boss->done(0);
}
void member_BFileDialog::procCancleClicked()
{
	boss->hide();
}

